﻿//===============================================================================
// Microsoft patterns & practices Enterprise Library Contribution
// Query Application Block
//===============================================================================

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Globalization;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration.Manageability;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration.Manageability.Adm;
using EntLibContrib.Query.Properties;

namespace EntLibContrib.Query.Configuration.Manageability
{
	/// <summary>
	/// Represents the behaviour required to provide Group Policy updates and to publish the
	/// <see cref="ConfigurationSetting"/> instances associated to a <see cref="QuerySettings"/> instance
	/// and its internal configuration elements.
	/// </summary>
	public sealed class QuerySettingsManageabilityProvider
		: ConfigurationSectionManageabilityProviderBase<QuerySettings>
	{
		#region Constants
		/// <summary>
		/// The name of the commands property.
		/// </summary>
		public const string CommandsKeyName = "commands";

		/// <summary>
		/// The name of the parameter sets property.
		/// </summary>
		public const string ParameterSetsKeyName = "parameterSets";

		/// <summary>
		/// The name of the parameters property.
		/// </summary>
		public const string ParametersKeyName = "parameters";

		/// <summary>
		/// The name of the queries property.
		/// </summary>
		public const string QueriesKeyName = "queries";
		#endregion

		#region Properties
		/// <summary>
		/// Gets the name of the category that represents the whole configuration section.
		/// </summary>
		protected override string SectionCategoryName
		{
			get { return InternalResources.QuerySectionCategoryName; }
		}

		/// <summary>
		/// Gets the name of the managed configuration section.
		/// </summary>
		protected override string SectionName
		{
			get { return QuerySettings.SectionName; }
		}
		#endregion

		#region Construction
		/// <summary>
		/// 	<para>This method supports the Enterprise Library Manageability Extensions infrastructure and
		/// is not intended to be used directly from your code.</para>
		/// 	<para>Initializes a new instance of the <see cref="QuerySettingsManageabilityProvider"/> class
		/// with a given set of manageability providers to use when dealing with the configuration for
		/// commands, parameter sets and queries.</para>
		/// </summary>
		/// <param name="subProviders">The mapping from configuration element type to <see cref="ConfigurationElementManageabilityProvider"/>.</param>
		public QuerySettingsManageabilityProvider(IDictionary<Type, ConfigurationElementManageabilityProvider> subProviders)
			: base(subProviders)
		{
			QuerySettingsWmiMapper.RegisterWmiTypes();
		}
		#endregion

		#region ADM Generation
		/// <summary>
		/// Adds the ADM instructions that describe the policies that can be used to override the configuration
		/// information represented by a configuration section.
		/// </summary>
		/// <param name="contentBuilder">The <see cref="T:AdmContentBuilder"/> to which the Adm instructions are to be appended.</param>
		/// <param name="configurationSection">The configuration section instance.</param>
		/// <param name="configurationSource">The configuration source from where to get additional configuration
		/// information, if necessary.</param>
		/// <param name="sectionKey">The root key for the section's policies.</param>
		protected override void AddAdministrativeTemplateDirectives(AdmContentBuilder contentBuilder,
																																QuerySettings configurationSection,
																																IConfigurationSource configurationSource,
																																string sectionKey)
		{
			AddElementsPolicies<CustomCommandData>(contentBuilder,
																						configurationSection.Commands,
																						configurationSource,
																						sectionKey + @"\" + CommandsKeyName,
																						InternalResources.CommandsCategoryName);
			AddParameterSetsPolicies(contentBuilder,
															configurationSection.ParameterSets,
															configurationSource,
															sectionKey + @"\" + ParameterSetsKeyName,
															InternalResources.ParameterSetsCategoryName);
			AddElementsPolicies<QueryDataBase>(contentBuilder,
																				configurationSection.Queries,
																				configurationSource,
																				sectionKey + @"\" + QueriesKeyName,
																				InternalResources.QueriesCategoryName);
		}

		/// <summary>
		/// Adds the parameter sets policies.
		/// </summary>
		/// <param name="contentBuilder">The content builder.</param>
		/// <param name="parameterSetElements">The parameter set elements.</param>
		/// <param name="configurationSource">The configuration source.</param>
		/// <param name="parentKey">The parent key.</param>
		/// <param name="categoryName">Name of the category.</param>
		private void AddParameterSetsPolicies(AdmContentBuilder contentBuilder,
																					NamedElementCollection<ParameterSetData> parameterSetElements,
																					IConfigurationSource configurationSource,
																					string parentKey,
																					string categoryName)
		{
			contentBuilder.StartCategory(categoryName);

			foreach (ParameterSetData parameterSetData in parameterSetElements)
			{
				contentBuilder.StartCategory(parameterSetData.Name);
				AddElementsPolicies<ParameterData>(contentBuilder,
																					parameterSetData.Parameters,
																					configurationSource,
																					parentKey + @"\" + parameterSetData.Name + @"\" + ParametersKeyName,
																					InternalResources.ParametersCategoryName);
				contentBuilder.EndCategory();
			}

			contentBuilder.EndCategory();
		}
		#endregion

		#region Manageability Support
		/// <summary>
		/// Creates the <see cref="T:Microsoft.Practices.EnterpriseLibrary.Common.Configuration.Manageability.ConfigurationSetting"/> instances that describe the <paramref name="configurationSection"/>.
		/// </summary>
		/// <param name="configurationSection">The configuration section that must be managed.</param>
		/// <param name="wmiSettings">A collection to where the generated WMI objects are to be added.</param>
		protected override void GenerateWmiObjectsForConfigurationSection(QuerySettings configurationSection, ICollection<ConfigurationSetting> wmiSettings)
		{
			// nothing to generate here
		}

		/// <summary>
		/// Overrides the <paramref name="configurationSection"/>'s properties with the Group Policy values from
		/// the registry.
		/// </summary>
		/// <param name="configurationSection">The configuration section that must be managed.</param>
		/// <param name="policyKey">The <see cref="T:Microsoft.Practices.EnterpriseLibrary.Common.Configuration.Manageability.IRegistryKey"/> which holds the Group Policy overrides.</param>
		/// <remarks>Subclasses implementing this method must retrieve all the override values from the registry
		/// before making modifications to the <paramref name="configurationSection"/> so any error retrieving
		/// the override values will cancel policy processing.</remarks>
		protected override void OverrideWithGroupPoliciesForConfigurationSection(QuerySettings configurationSection, IRegistryKey policyKey)
		{
			// nothing to configure here
		}

		/// <summary>
		/// Overrides the <paramref name="configurationSection"/>'s configuration elements' properties 
		/// with the Group Policy values from the registry, if any, and creates the <see cref="ConfigurationSetting"/> 
		/// instances that describe these configuration elements.
		/// </summary>
		/// <param name="configurationSection">The configuration section that must be managed.</param>
		/// <param name="readGroupPolicies"><see langword="true"/> if Group Policy overrides must be applied; otherwise, 
		/// <see langword="false"/>.</param>
		/// <param name="machineKey">The <see cref="IRegistryKey"/> which holds the Group Policy overrides for the 
		/// configuration section at the machine level, or <see langword="null"/> 
		/// if there is no such registry key.</param>
		/// <param name="userKey">The <see cref="IRegistryKey"/> which holds the Group Policy overrides for the 
		/// configuration section at the user level, or <see langword="null"/> 
		/// if there is no such registry key.</param>
		/// <param name="generateWmiObjects"><see langword="true"/> if WMI objects must be generated; otherwise, 
		/// <see langword="false"/>.</param>
		/// <param name="wmiSettings">A collection to where the generated WMI objects are to be added.</param>
		protected override void OverrideWithGroupPoliciesAndGenerateWmiObjectsForConfigurationElements(QuerySettings configurationSection,
																																																	bool readGroupPolicies,
																																																	IRegistryKey machineKey,
																																																	IRegistryKey userKey,
																																																	bool generateWmiObjects,
																																																	ICollection<ConfigurationSetting> wmiSettings)
		{
			OverrideWithGroupPoliciesAndGenerateWmiObjectsForElementCollection(configurationSection.Commands,
																																				CommandsKeyName,
																																				readGroupPolicies,
																																				machineKey,
																																				userKey,
																																				generateWmiObjects,
																																				wmiSettings);
			OverrideWithGroupPoliciesAndGenerateWmiObjectsForParameterSets(configurationSection.ParameterSets,
																																		ParameterSetsKeyName,
																																		readGroupPolicies,
																																		machineKey,
																																		userKey,
																																		generateWmiObjects,
																																		wmiSettings);
			OverrideWithGroupPoliciesAndGenerateWmiObjectsForElementCollection(configurationSection.Queries,
																																				QueriesKeyName,
																																				readGroupPolicies,
																																				machineKey,
																																				userKey,
																																				generateWmiObjects,
																																				wmiSettings);
		}

		/// <summary>
		/// Overrides settings with the group policies and generates WMI objects for parameter sets.
		/// </summary>
		/// <param name="parameterSetElements">The parameter set elements.</param>
		/// <param name="keyName">Name of the key.</param>
		/// <param name="readGroupPolicies">if set to <c>true</c> [read group policies].</param>
		/// <param name="machineKey">The machine key.</param>
		/// <param name="userKey">The user key.</param>
		/// <param name="generateWmiObjects">if set to <c>true</c> [generate WMI objects].</param>
		/// <param name="wmiSettings">The WMI settings.</param>
		private void OverrideWithGroupPoliciesAndGenerateWmiObjectsForParameterSets(NamedElementCollection<ParameterSetData> parameterSetElements,
																																								string keyName,
																																								bool readGroupPolicies,
																																								IRegistryKey machineKey,
																																								IRegistryKey userKey,
																																								bool generateWmiObjects,
																																								ICollection<ConfigurationSetting> wmiSettings)
		{
			if (readGroupPolicies)
			{
				List<ParameterSetData> elementsToRemove = new List<ParameterSetData>();

				IRegistryKey machineParameterSetsKey = null;
				IRegistryKey userParameterSetsKey = null;

				try
				{
					LoadRegistrySubKeys(keyName, machineKey, userKey, out machineParameterSetsKey, out userParameterSetsKey);

					foreach (ParameterSetData parameterSetData in parameterSetElements)
					{
						IRegistryKey machineParameterSetKey = null;
						IRegistryKey userParameterSetKey = null;

						try
						{
							LoadRegistrySubKeys(parameterSetData.Name, machineParameterSetsKey, userParameterSetsKey, out machineParameterSetKey, out userParameterSetKey);

							IRegistryKey policyKey = machineParameterSetKey != null ? machineParameterSetKey : userParameterSetKey;
							if (policyKey != null && policyKey.IsPolicyKey && !policyKey.GetBoolValue(PolicyValueName).Value)
								elementsToRemove.Add(parameterSetData);
							else
								OverrideWithGroupPoliciesAndGenerateWmiObjectsForElementCollection(parameterSetData.Parameters,
																																									ParametersKeyName,
																																									readGroupPolicies,
																																									machineParameterSetKey,
																																									userParameterSetKey,
																																									generateWmiObjects,
																																									wmiSettings);
						}
						finally
						{
							ReleaseRegistryKeys(machineParameterSetKey, userParameterSetKey);
						}
					}
				}
				finally
				{
					ReleaseRegistryKeys(machineParameterSetsKey, userParameterSetsKey);
				}

				// remove disabled elements
				foreach (ParameterSetData element in elementsToRemove)
				{
					parameterSetElements.Remove(element.Name);
				}
			}
		}
		#endregion
	}
}
